class i2c_mst_drv extends uvm_driver #(apb_trans);
    virtual i2c_if vif;          // DUT virtual interface
    
    `uvm_component_utils(i2c_mst_drv)

    function new(string name, uvm_component parent);
        super.new(name, parent);
        `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH)
    endfunction: new

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH)

        uvm_config_db#(virtual i2c_if )::get(this, "", "i2c_if", vif);
        if (vif == null) begin
            `uvm_fatal("CFGERR", "Interface for Driver not set");
        end
    endfunction: build_phase

    virtual task run_phase(uvm_phase phase);
        `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH)
        wait(vif.rst==0);
        forever begin
            seq_item_port.get_next_item(req);
            drive(req);
            `uvm_info("DRV_RUN", {"\n", req.sprint()}, UVM_MEDIUM)
            seq_item_port.item_done();
        end
    endtask: run_phase


    virtual task pre_reset_phase(uvm_phase phase);
        super.pre_reset_phase(phase);
        `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH)
        phase.raise_objection(this);

            vif.drvclk.sda <= '1; // pull up according i2c protocol
            vif.drvclk.scl <= '1; // pull up according i2c protocol

        phase.drop_objection(this);
    endtask: pre_reset_phase

    virtual task drive(i2c_trans req);
        `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH)
        
        case(req.kind)
            i2c_trans::READ: begin
                @vif.drvclk;
                    // do something here
                    vif.drvclk.sda <= '0; // pull low to start condition
                //send address frame
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[7];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[6];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[5];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[4];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[3];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[2];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[1];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[0];
                // R/W bits, 1 for reading, 0 for write
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= '0;
                //read ack bit, should be 0
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    aaaaa <= vif.drvclk.sda; //read ack bit
                //folloing should be 32 bit data
                //divide into 8 bits per data frame
                @
            end

            i2c_trans::WRITE: begin
                @vif.drvclk;
                    // do something here
                    vif.drvclk.sda <= '0; // pull low to start condition
                //send address frame
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[7];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[6];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[5];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[4];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[3];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[2];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[1];
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= req.addr[0];
                // R/W bits, 1 for reading, 0 for write
                @vif.drvclk;
                    vif.drvclk.scl <= ~vif.drvclk.scl;
                    vif.drvclk.sda <= '1;
            end
            default: begin `uvm_fatal("ERROR", "Not a valid Command"); end
        endcase
    endtask: drive
endclass